// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h"

#include "base/memory/raw_ptr.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_syncable_service.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/test_sync_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"

namespace {

constexpr SkColor kNewProfileColor = SK_ColorRED;
constexpr SkColor kSyncedProfileColor = SK_ColorBLUE;
const char kTestingProfileName[] = "testing_profile";

class FakeThemeService : public ThemeService {
 public:
  explicit FakeThemeService(const ThemeHelper& theme_helper)
      : ThemeService(nullptr, theme_helper) {}

  void SetThemeSyncableService(ThemeSyncableService* theme_syncable_service) {
    theme_syncable_service_ = theme_syncable_service;
  }

  // ThemeService:
  void DoSetTheme(const extensions::Extension* extension,
                  bool suppress_infobar) override {
    using_default_theme_ = false;
    color_ = 0;
  }

  void BuildAutogeneratedThemeFromColor(SkColor color) override {
    color_ = color;
    using_default_theme_ = false;
  }

  void UseDefaultTheme() override {
    using_default_theme_ = true;
    color_ = 0;
  }

  bool UsingDefaultTheme() const override { return using_default_theme_; }

  SkColor GetAutogeneratedThemeColor() const override { return color_; }

  ThemeSyncableService* GetThemeSyncableService() const override {
    return theme_syncable_service_;
  }

 private:
  raw_ptr<ThemeSyncableService> theme_syncable_service_ = nullptr;
  bool using_default_theme_ = true;
  SkColor color_ = 0;
};

class ProfileCustomizationBubbleSyncControllerTest : public testing::Test {
 public:
  using Outcome = ProfileCustomizationBubbleSyncController::Outcome;
  ProfileCustomizationBubbleSyncControllerTest()
      : testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
        fake_theme_service_(theme_helper_),
        theme_syncable_service_(nullptr, &fake_theme_service_) {}

  void SetUp() override {
    fake_theme_service_.SetThemeSyncableService(&theme_syncable_service_);

    ASSERT_TRUE(testing_profile_manager_.SetUp());
    testing_profile_ =
        testing_profile_manager_.CreateTestingProfile(kTestingProfileName);

    testing_view_ = std::make_unique<views::View>();
  }

  void ApplyColorAndShowBubbleWhenNoValueSynced(
      ProfileCustomizationBubbleSyncController::ShowBubbleCallback
          show_bubble_callback) {
    ProfileCustomizationBubbleSyncController::
        ApplyColorAndShowBubbleWhenNoValueSyncedForTesting(
            testing_profile_, testing_view_.get(), &test_sync_service_,
            &fake_theme_service_, std::move(show_bubble_callback),
            kNewProfileColor);
  }

  void SetSyncedProfileColor() {
    fake_theme_service_.BuildAutogeneratedThemeFromColor(kSyncedProfileColor);
  }

  void SetSyncedProfileTheme() {
    fake_theme_service_.DoSetTheme(nullptr, false);
  }

  void DeleteTestingProfile() {
    testing_profile_manager_.DeleteTestingProfile(kTestingProfileName);
  }

  void DeleteTestingView() { testing_view_.reset(); }

  void NotifyOnSyncStarted(bool waiting_for_extension_installation = false) {
    theme_syncable_service_.NotifyOnSyncStartedForTesting(
        waiting_for_extension_installation
            ? ThemeSyncableService::ThemeSyncState::
                  kWaitingForExtensionInstallation
            : ThemeSyncableService::ThemeSyncState::kApplied);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  syncer::TestSyncService test_sync_service_;

 private:
  raw_ptr<Profile> testing_profile_ = nullptr;
  TestingProfileManager testing_profile_manager_;
  std::unique_ptr<views::View> testing_view_;
  FakeThemeService fake_theme_service_;
  ThemeSyncableService theme_syncable_service_;
  ThemeHelper theme_helper_;
};

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldShowWhenSyncGetsDefaultTheme) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kShowBubble));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  NotifyOnSyncStarted();
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldShowWhenSyncDisabled) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kShowBubble));

  test_sync_service_.SetDisableReasons(
      syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenSyncGetsCustomColor) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kSkipBubble));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  SetSyncedProfileColor();
  NotifyOnSyncStarted();
}

// Regression test for crbug.com/1213109.
TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenSyncGetsCustomColorBeforeStarting) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kSkipBubble));

  // Set up theme sync before the bubble controller gets created.
  SetSyncedProfileColor();
  NotifyOnSyncStarted();

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenSyncGetsCustomTheme) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kSkipBubble));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  SetSyncedProfileTheme();
  NotifyOnSyncStarted();
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenSyncGetsCustomThemeToInstall) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kSkipBubble));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  NotifyOnSyncStarted(/*waiting_for_extension_installation=*/true);
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenSyncHasCustomPasshrase) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kSkipBubble));

  test_sync_service_.SetPassphraseRequired(true);
  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  test_sync_service_.FireStateChanged();
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenProfileGetsDeleted) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kAbort));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  DeleteTestingProfile();
}

TEST_F(ProfileCustomizationBubbleSyncControllerTest,
       ShouldNotShowWhenViewGetsDeleted) {
  base::MockCallback<base::OnceCallback<void(Outcome)>> show_bubble;
  EXPECT_CALL(show_bubble, Run(Outcome::kAbort));

  ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get());
  DeleteTestingView();
}

}  // namespace
